#pragma once

#include "Sock.hpp"
#include "Epoll.hpp"

#include <string>
#include <functional>
#include <unordered_map>


void SplitMessage(std::string &message, std::vector<std::string> &v);

namespace FangZhang
{
    class Connection;
    class EpollServerET;

    using func_t = std::function<void(Connection*)>;

    class Connection
    {
    public:
        Connection(int sock = -1, EpollServerET * prsvr = nullptr) // 初始化给-1，表示不存在
            : _sock(sock)
            , _prsvr(prsvr) // 这个回指指针等会有用，这里暂时体现不出来用处
        {}

        void SetCallBacks(func_t readCallBack, func_t sendCallBack, func_t exceptCallBack)
        { // 给每个回调赋值
            _readCallBack = readCallBack; 
            _sendCallBack = sendCallBack;
            _exceptCallBack = exceptCallBack;
        }

    public:
        int _sock; // 专门通信的sock
        std::string _readBuff; // 接收缓冲区    |这里的缓冲区有点小BUG，就是不能
        std::string _sendBuff; // 发送缓冲区    |处理二进制流，但是文本还是能处理的

        func_t _readCallBack; // 读取回调
        func_t _sendCallBack; // 发送回调
        func_t _exceptCallBack; // 异常回调

        EpollServerET *_prsvr; // 回指指针，等会有大用
        
        //time_t _lastTime = time(nullptr); // 保活机制
    };

    class EpollServerET
    {
        // 回调
        using callBack_t = std::function<void(Connection*, std::string&)>;

        const static int REVENT_NUM = 100;
    public:
        EpollServerET(int port = 8080)
            : _port(port)
            , _maxReventNum(REVENT_NUM)
        {
            // 创建套接字
            _listenSock = Sock::Socket();
            Sock::Bind(_listenSock, _port); // 绑定
            Sock::Listen(_listenSock); // 设置监听状态

            _poll.CreateEpoll(); // 创建epoll模型

            // 将listenSock添加到epoll模型中
            AddSock(_listenSock, std::bind(&EpollServerET::Accepter, this, std::placeholders::_1), nullptr, nullptr);

            // 给就绪数组开空间
            _revents = new epoll_event[_maxReventNum];
        }

        // 启动服务器
        void Start(callBack_t cb)
        {
            _cb = cb;
            while(1)
            {
                // IsAlive(); 保活机制
                LoopOnce();
            }
        }

        ~EpollServerET()
        {
            if(_listenSock >= 0) close(_listenSock);

            if(_revents) delete[] _revents;
        }

        void EnableReadWrite(Connection* conn, bool isRead, bool isWrite)
        {
            uint32_t events = (isRead ? EPOLLIN : 0) | (isWrite ? EPOLLOUT : 0);
            _poll.ModEpoll(conn->_sock, events);
        }

    private:
        void AddSock(int sock, func_t readCallBack, func_t sendCallBack, func_t exceptCallBack)
        { // 添加文件描述符的同时也将对应的回调函数处理好
            // 将sock设置为非阻塞的
            Sock::NonBlock(sock);
            // 创建sock对应的Connection对象，并将字段填充好
            Connection* conn = new Connection(sock, this);
            conn->SetCallBacks(readCallBack, sendCallBack, exceptCallBack);

                                              /*设置Epoll为ET模式*/
            _poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET); // 任何多路转接服务器，默认打开对读事件的关心
            
            _sockCon.insert(std::make_pair(sock, conn)); // 哈希映射中记录一下
        }

        void LoopOnce()
        {
            // 这里直接以阻塞方式等待
            int timeout = -1;
            int n = _poll.WaitEpoll(_revents, _maxReventNum, timeout);
            if(n == -1)
            { // 调用出错
                LogMessage(WARING, "epoll_wait fail, errno[%d]:%s", errno, strerror(errno));
            }
            else if(n == 0)
            { // 等待超时
                std::cout << "time out" << std::endl;
            }
            else
            { // 等待成功
                for(int i = 0; i < n; ++i)
                { // 遍历就绪的事件
                    int sock = _revents[i].data.fd;
                    uint32_t events = _revents[i].events;

                    if(events & EPOLLIN)
                    { // 读事件就绪
                        if(isExist(sock) && _sockCon[sock]->_readCallBack != nullptr)
                        { // 判断映射中是否有这个sock，有了再判断对应的Connection中的读回调是否就绪
                            _sockCon[sock]->_readCallBack(_sockCon[sock]);
                        }
                    }
                    
                    if(events & EPOLLOUT)
                    { // 写事件就绪
                        if(isExist(sock) && _sockCon[sock]->_sendCallBack != nullptr)
                        { // 判断映射中是否有这个sock，有了再判断对应的Connection中的写回调是否就绪
                            _sockCon[sock]->_sendCallBack(_sockCon[sock]);
                        }
                    }
                }
            }
        }

        void Accepter(Connection* conn)
        {
            while(1)
            {
                std::string clientIp;
                uint16_t clientPort;
                int sock = Sock::Accept(_listenSock, clientIp, clientPort);
                if(sock >= 0)
                { // 接收到连接
                    // 获取到sock之后就添加到epoll模型、创建一个Connection对象、存入哈希映射中
                    AddSock(sock, 
                        std::bind(&EpollServerET::Reader, this, std::placeholders::_1), 
                        std::bind(&EpollServerET::Writer, this, std::placeholders::_1), 
                        std::bind(&EpollServerET::Excepter, this, std::placeholders::_1)
                        ); // 直接调用AddSock就能实现
                    // 对于listenSock只用关心读，也就是接收连接，但对于用来IO的sock读、写、异常都要关心
                    // 所以需要我们在类内提供三个专门负责通信时进行处理读、写、异常的函数
                }
                else
                { // 没接收到连接
                    // 读取完毕，没有连接了，就不用再接收了。accept就会将错误码设置为EAGAIN
                    if(errno == EWOULDBLOCK || errno == EAGAIN) break;
                    // 遭到信号中断当前读取，所以是本次读取未完成，还要继续读取
                    else if(errno == EINTR) continue; 
                    else
                    { // 这里才是真正的异常
                        LogMessage(ERROR, _F, _L, "server accept connection failed");
                        break;
                    }
                }
            }
        }

        void Reader(Connection* conn)
        { // 专门负责读的函数
            
            //conn->_lastTime = time(nullptr); // 更新一下最后访问的时间

            int sock = conn->_sock;
            bool err = false;
            while(1)
            {
                char buff[1024] = {0};
                // 这里如果用的是recv(sock, buff, sizeof(buff) - 1, 0)，最后一个flag设置成0也是没关系的
                // 因为前面已经将sock设置成非阻塞的了
                int n = read(sock, buff, sizeof(buff) - 1);
                if(n > 0)
                { // 读取到数据
                    // 服务端只负责读取数据，不要处理数据，处理数据是上层业务负责的
                    buff[n - 2] = 0;
                    conn->_readBuff += buff; 
                    // 服务端将数据放到接受缓冲区中，然后上层再根据接收缓冲区中的数据进行处理
                    printf("%s : %d\n", conn->_readBuff.c_str(), conn->_readBuff.size());
                }
                else if(n == 0)
                { // 对端关闭连接
                    LogMessage(NORMAL, _F, _L, "client closed, sock[%d]", sock);
                    conn->_exceptCallBack(conn); // 交给异常来处理
                    err = true;
                    break;
                }
                else
                { // 读取失败
                    if(errno == EWOULDBLOCK || errno == EAGAIN) 
                    {
                        break;
                    }
                    else if(errno == EINTR) continue;
                    else
                    {
                        conn->_exceptCallBack(conn); // 交给异常来处理
                        LogMessage(ERROR, _F, _L,  "read err, errno[%d]::%s", errno, strerror(errno));
                        break;
                    }
                }
            }

            if(!err)
            { // 这里对端没有关闭连接，处理还在的连接中的数据
                std::vector<std::string> vs;
                SplitMessage(conn->_readBuff, vs); // 取完整报文
                for(auto &msg : vs)
                { // 走到这里一定是有完整报文的，上层自行处理
                    _cb(conn, msg);
                }
            }
        }

        void Writer(Connection* conn)
        { // 专门负责写的函数
            while(1)
            {
                int res = write(conn->_sock, conn->_sendBuff.c_str(), conn->_sendBuff.size());                
                if(res > 0)
                {
                    conn->_sendBuff.erase(0, res); // 去掉用户发送缓冲区已经发送的数据
                    if(conn->_sendBuff.size() == 0) break; // 如果用户发送缓冲区已经空了就不要再发送了
                }
                else
                { // res <= 0
                    if(errno == EWOULDBLOCK || errno == EAGAIN) break;//说明发送缓冲区满了，那就下一次再发送
                    else if(errno == EINTR) continue; // 被信号打断了，回来之后继续发送
                    else
                    { // 真的出错了
                        LogMessage(ERROR, _F, _L, "write err, errno[%d]:%s", errno, strerror(errno));
                        conn->_exceptCallBack(conn);
                        return;
                    }
                }
            }
            // 此处可能是用户发送缓冲区空了，也可能是内核缓冲区满了
            if(conn->_sendBuff.empty())
            { // 用户发送缓冲区空了，直接去掉对当前sock写的关心
                EnableReadWrite(conn, true, false);
            }
            else
            {
                EnableReadWrite(conn, true, true);
            }
        }

        void Excepter(Connection* conn)
        { // 专门负责处理异常的函数
            // 先判断sock在不在哈希中，不在就直接返回
            if(!isExist(conn->_sock)) return; 
            // 删除Epoll中的对该sock的关心
            _poll.DelSockFromEpoll(conn->_sock);
            // 去掉映射
            _sockCon.erase(conn->_sock);
            // 关闭文件描述符
            close(conn->_sock);
            // 释放Connection对象的空间，不然内存泄漏了
            delete conn;
        }

        bool isExist(int sock)
        {
            auto it = _sockCon.find(sock);
            if(it == _sockCon.end())
            {
                return false;
            }

            return true;
        }

    private:
        int _listenSock;
        uint16_t _port;
        Epoll _poll; // 用来进行epoll的相关操作
        std::unordered_map<int, Connection*> _sockCon; // 存放sock与对应Connection对象地址的映射
        epoll_event *_revents; // 就绪的文件描述符事件数组
        int _maxReventNum; // 数组元素个数
        callBack_t _cb; // 上层处理完整报文的方法
    };
}
